/*=========================================================================

  Library:   HMP

  Copyright (c) Kitware Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

  =========================================================================*/

// Qt includes
#include <QDebug>
#include <QMetaType>
#include <QObject>
#include <QState>

// HMP includes
#include "WorkflowStep.h"
#include "WorkflowStep_p.h"
#include "Workflow.h"

// STD includes
#include <iostream>

// --------------------------------------------------------------------------
// WorkflowStepPrivate methods

// --------------------------------------------------------------------------
WorkflowStepPrivate::WorkflowStepPrivate(WorkflowStep& object)
  :q_ptr(&object)
{
  qRegisterMetaType<WorkflowStep*>("WorkflowStep*");
  this->mWorkflow = 0;

  this->WidgetType = false;

  this->HasValidateCommand = false;
  this->HasOnEntryCommand = false;
  this->HasOnExitCommand = false;

  // Create state
  this->ProcessingState = new QState();
  this->ValidationState = new QState();

  // Create 'validation' transition
  this->ValidationTransition =
      new WorkflowIntrastepTransition(WorkflowIntrastepTransition::ValidationTransition);
  this->ValidationTransition->setTargetState(this->ValidationState);
  this->ProcessingState->addTransition(this->ValidationTransition);

  // Create 'failed validation' transation
  this->ValidationFailedTransition = 0;
  this->ValidationFailedTransition =
      new WorkflowIntrastepTransition(WorkflowIntrastepTransition::ValidationFailedTransition);
  this->ValidationFailedTransition->setTargetState(this->ProcessingState);
  this->ValidationState->addTransition(this->ValidationFailedTransition);
}

// --------------------------------------------------------------------------
WorkflowStepPrivate::~WorkflowStepPrivate()
{
  if (!this->ValidationState.isNull())
    {
    delete this->ValidationState;
    }
  if (!this->ProcessingState.isNull())
    {
    delete this->ProcessingState;
    }

  // If we delete the states, then Qt will handle deleting the transitions
}

// --------------------------------------------------------------------------
void WorkflowStepPrivate::validationCompleteInternal(bool validationResults, const QString& branchId)const
{
  emit validationComplete(validationResults, branchId);
}

// --------------------------------------------------------------------------
void WorkflowStepPrivate::onEntryCompleteInternal()const
{
  emit onEntryComplete();
}

// --------------------------------------------------------------------------
void WorkflowStepPrivate::onExitCompleteInternal()const
{
  emit onExitComplete();
}

// --------------------------------------------------------------------------
void WorkflowStepPrivate::invokeValidateCommandInternal(const QString& desiredBranchId)const
{
  emit invokeValidateCommand(desiredBranchId);
}

// --------------------------------------------------------------------------
void WorkflowStepPrivate::invokeOnEntryCommandInternal(const WorkflowStep* comingFrom, const WorkflowInterstepTransition::InterstepTransitionType transitionType)const
{
  emit invokeOnEntryCommand(comingFrom, transitionType);
}

// --------------------------------------------------------------------------
void WorkflowStepPrivate::invokeOnExitCommandInternal(const WorkflowStep* goingTo, const WorkflowInterstepTransition::InterstepTransitionType transitionType)const
{
  emit invokeOnExitCommand(goingTo, transitionType);
}

// --------------------------------------------------------------------------
// WorkflowStep methods

// --------------------------------------------------------------------------
WorkflowStep::WorkflowStep(): d_ptr(new WorkflowStepPrivate(*this))
{
}

// --------------------------------------------------------------------------
WorkflowStep::WorkflowStep(const QString& newId)
  : d_ptr(new WorkflowStepPrivate(*this))
{
  Q_D(WorkflowStep);
  d->Id = newId;
}

// --------------------------------------------------------------------------
WorkflowStep::WorkflowStep(WorkflowStepPrivate * pimpl,
                                 const QString& newId):d_ptr(pimpl)
{
  Q_D(WorkflowStep);
  d->Id = newId;
}

// --------------------------------------------------------------------------
WorkflowStep::~WorkflowStep()
{
}

// --------------------------------------------------------------------------
HMP_GET_CPP(WorkflowStep, Workflow*, workflow, mWorkflow);
HMP_SET_CPP(WorkflowStep, Workflow*, setWorkflow, mWorkflow);

// --------------------------------------------------------------------------
HMP_GET_CPP(WorkflowStep, QString, id, Id);

// --------------------------------------------------------------------------
void WorkflowStep::setId(const QString& newId)
{
  Q_D(WorkflowStep);
  if (d->mWorkflow && d->mWorkflow->hasStep(newId) && !this->id().isEmpty())
    {
    qWarning() << QString("WorkflowStep - Failed to change id from '%1' to '%2' - "
                          "Step already added to a workflow !").arg(this->id()).arg(newId);
    return;
    }
  d->Id = newId;
}

// --------------------------------------------------------------------------
HMP_GET_CPP(WorkflowStep, QString, name, Name);
HMP_SET_CPP(WorkflowStep, const QString&, setName, Name);

// --------------------------------------------------------------------------
HMP_GET_CPP(WorkflowStep, QString, description, Description);
HMP_SET_CPP(WorkflowStep, const QString&, setDescription, Description);

// --------------------------------------------------------------------------
HMP_GET_CPP(WorkflowStep, QString, statusText, StatusText);
HMP_SET_CPP(WorkflowStep, const QString&, setStatusText, StatusText);

// --------------------------------------------------------------------------
HMP_GET_CPP(WorkflowStep, bool, hasValidateCommand, HasValidateCommand);
HMP_SET_CPP(WorkflowStep, bool, setHasValidateCommand, HasValidateCommand);

// --------------------------------------------------------------------------
HMP_GET_CPP(WorkflowStep, bool, hasOnEntryCommand, HasOnEntryCommand);
HMP_SET_CPP(WorkflowStep, bool, setHasOnEntryCommand, HasOnEntryCommand);

// --------------------------------------------------------------------------
HMP_GET_CPP(WorkflowStep, bool, hasOnExitCommand, HasOnExitCommand);
HMP_SET_CPP(WorkflowStep, bool, setHasOnExitCommand, HasOnExitCommand);

// --------------------------------------------------------------------------
HMP_GET_CPP(WorkflowStep, QState*, processingState, ProcessingState);
HMP_GET_CPP(WorkflowStep, QState*, validationState, ValidationState);

// --------------------------------------------------------------------------
HMP_GET_CPP(WorkflowStep, WorkflowIntrastepTransition*, validationTransition, ValidationTransition);
HMP_GET_CPP(WorkflowStep, WorkflowIntrastepTransition*,
            validationFailedTransition, ValidationFailedTransition);

// --------------------------------------------------------------------------
HMP_GET_CPP(WorkflowStep, bool, isWidgetType, WidgetType);

// --------------------------------------------------------------------------
QObject* WorkflowStep::WorkflowStepQObject()
{
  Q_D(WorkflowStep);
  return d;
}

// --------------------------------------------------------------------------
void WorkflowStep::validationComplete(bool validationResults, const QString& branchId)const
{
  Q_D(const WorkflowStep);
  d->validationCompleteInternal(validationResults, branchId);
}

// --------------------------------------------------------------------------
void WorkflowStep::onEntryComplete()const
{
  Q_D(const WorkflowStep);
  d->onEntryCompleteInternal();
}

// --------------------------------------------------------------------------
void WorkflowStep::onExitComplete()const
{
  Q_D(const WorkflowStep);
  d->onExitCompleteInternal();
}

// --------------------------------------------------------------------------
void WorkflowStep::invokeValidateCommand(const QString& desiredBranchId)const
{
  Q_D(const WorkflowStep);
  d->invokeValidateCommandInternal(desiredBranchId);
}

// --------------------------------------------------------------------------
void WorkflowStep::invokeOnEntryCommand(const WorkflowStep* comingFrom, const WorkflowInterstepTransition::InterstepTransitionType transitionType)const
{
  Q_D(const WorkflowStep);
  d->invokeOnEntryCommandInternal(comingFrom, transitionType);
}

// --------------------------------------------------------------------------
void WorkflowStep::invokeOnExitCommand(const WorkflowStep* goingTo, const WorkflowInterstepTransition::InterstepTransitionType transitionType)const
{
  Q_D(const WorkflowStep);
  d->invokeOnExitCommandInternal(goingTo, transitionType);
}

// --------------------------------------------------------------------------
void WorkflowStep::validate(const QString& desiredBranchId)
{
  Q_D(WorkflowStep);
  if (this->workflow()->verbose())
    {
    qDebug() << QString("validate - validating the input from %1").arg(d->Name);
    }

  this->validationComplete(true, desiredBranchId);
}


// --------------------------------------------------------------------------
void WorkflowStep::onEntry(const WorkflowStep* comingFrom,
                              const WorkflowInterstepTransition::InterstepTransitionType transitionType)
{
  Q_UNUSED(comingFrom);
  Q_UNUSED(transitionType);

  // Signals that we are finished
  this->onEntryComplete();
}

// --------------------------------------------------------------------------
void WorkflowStep::onExit(const WorkflowStep* goingTo,
                             const WorkflowInterstepTransition::InterstepTransitionType transitionType)
{
  Q_UNUSED(goingTo);
  Q_UNUSED(transitionType);

 // Signals that we are finished
 this->onExitComplete();
}
